package com.ygl.consumer.lb;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author yanggld
 * @Date 2019/11/21-18:29
 * @Description
 * nacos 基于元数据的版本控制负载均衡策略
 */
@Slf4j
public class NacosMetadataLoadBalancerRule extends AbstractLoadBalancerRule {

	@Autowired
	private NacosDiscoveryProperties discoveryProperties;

	private static final String TARGET_VERSION = "target-version";
	private static final String VERSION = "version";

	private String clientName;

	@Override
	public void initWithNiwsConfig(IClientConfig clientConfig) {
		String clientName = clientConfig.getClientName();
		this.clientName = clientName;
	}

	@Override
	public Server choose(Object key) {
		// 获取配置文件中所配置的集群名称
		String clusterName = discoveryProperties.getClusterName();
		// 获取配置文件中所配置的元数据
		String targetVersion = discoveryProperties.getMetadata().get(TARGET_VERSION);

		// 获取服务发现的相关API
		NamingService namingService = discoveryProperties.namingServiceInstance();
	    try {
            // 获取该微服务的所有健康实例
            List<Instance> instances = namingService.selectInstances(clientName, true);
		    List<Instance> metadataMatchInstances = instances;
		    // 如果配置了版本映射，那么代表只调用元数据匹配的实例
		    if (StringUtils.isNotBlank(targetVersion)) {
			    // 过滤与版本元数据相匹配的实例，以实现版本控制
			    metadataMatchInstances  = instances.stream().filter( i -> Objects.equals(targetVersion, i.getMetadata().get(VERSION)))
					    .collect(Collectors.toList());

			    if (CollectionUtils.isEmpty(metadataMatchInstances)) {
				    log.warn("未找到元数据匹配的目标实例！请检查配置。targetVersion = {}, instance = {}",
						    targetVersion, instances);
				    return null;
			    }
		    }

		    List<Instance> clusterMetadataMatchInstances = metadataMatchInstances;
		    // 如果配置了集群名称，需筛选同集群下元数据匹配的实例
		    if (StringUtils.isNotBlank(clusterName)) {
			    // 过滤出相同集群下的所有实例
			    clusterMetadataMatchInstances = metadataMatchInstances.stream().filter( i -> Objects.equals(clusterName, i.getClusterName()))
					    .collect(Collectors.toList());

			    if (CollectionUtils.isEmpty(clusterMetadataMatchInstances)) {
				    clusterMetadataMatchInstances = metadataMatchInstances;
				    log.warn("发生跨集群调用。clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, targetVersion, clusterMetadataMatchInstances);
			    }
		    }

            // 基于随机权重的负载均衡算法，从实例列表中选取一个实例
            Instance instance = ExtendBalancer.getHostByRandomWeight2(clusterMetadataMatchInstances);
            log.info("选择的实例是：port = {}, instance = {}", instance.getPort(), instance);

            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("获取实例发生异常", e);
            return null;
        }
	}
}
